package main;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

import main.Instance.Weight;

import utils.Pair;

public class LiteralAlocatorAndMap {
	
	private Map<Integer, String> strongNodes;
	private Map<Integer, String> weakNodes;
	private Map<Integer, String> other;
	private HashMap<Pair<Integer, Integer>, Integer>[] pathLiteral;
	
	private Map<Integer, Integer> strongNodesToLits;
	private Map<Integer, Integer> weakNodesToLits;
	
	private int litCounter;
	private int zeroLiteral;
	private int numOfVertices;
	private int[] sumBits;
	private HashMap<Pair<Integer, Integer>, Integer> longEdgesToLits;
	private HashMap<Pair<Integer, Integer>, Integer> shortEdgesToLits;
	
	public LiteralAlocatorAndMap(int numOfVertices) {
		longEdgesToLits = new HashMap<Pair<Integer, Integer>, Integer>();
		shortEdgesToLits = new HashMap<Pair<Integer, Integer>, Integer>();
		strongNodes = new HashMap< Integer, String>();
		weakNodes = new HashMap< Integer, String>();
		other = new HashMap< Integer, String>();
		pathLiteral = new HashMap[numOfVertices-1];
		for (int i = 0; i < numOfVertices-1; i++) {
			pathLiteral[i] = new HashMap<>();
		}
		
		strongNodesToLits = new HashMap<Integer, Integer>();
		weakNodesToLits = new HashMap<Integer, Integer>();
		
		litCounter = 1;
		zeroLiteral = -1;
		this.numOfVertices = numOfVertices;
		sumBits = null;
	}
	
	public void setSumBits(int[] sumBits){
		this.sumBits = sumBits;
	}
	
	public int[] getSumBits(){
		return this.sumBits;
	}
	
	public Vector<int[]> generateNodesLits(){
		Vector<int[]> cnf = new Vector<int[]>();
		for (int i = 1; i <= numOfVertices; i++){
			int s = newStrongNode(i, i+" strong");
			int w = newWeakNode(i, i + " weak");
			int[] clause = {s,w};
			cnf.add(clause);
			clause = new int[]{-s,-w};
			cnf.add(clause);
		}
		return cnf;
	}
	
	public int newStrongNode(int node, String description){
		int newLit = litCounter++;
		strongNodes.put(newLit, description);
		strongNodesToLits.put(node, newLit);
		return newLit;
	}
	
	public int getStrongNodeLit(int node){
		return strongNodesToLits.get(node);
	}
	
	public int newWeakNode(int node, String description){
		int newLit = litCounter++;
		weakNodes.put(newLit, description);
		weakNodesToLits.put(node, newLit);
		return newLit;
	}
	
	public int getWeakNodeLit(int node){
		return weakNodesToLits.get(node);
	}
	
	public int[] getStrongNodeLits(){
		int[] strongNodeLits = new int[strongNodes.size()];
		int cnt=0;
		for (int lit : strongNodes.keySet()){
			strongNodeLits[cnt++] = lit;
		}
		return strongNodeLits;
	}
	
	public int newOther(String description){
		int newLit = litCounter++;
		other.put(newLit, description);
		return newLit;
	}
	
	private int newPathLiteral(Pair<Integer, Integer> pair, int length){
		int newLit = litCounter++;
		pathLiteral[length-1].put(pair, newLit);
		return newLit;
	}
	
	public int getPathLiteral(Pair<Integer, Integer> pair, int length){
		if (pathLiteral[length-1].containsKey(pair)){
			return pathLiteral[length-1].get(pair);
		}else {
			return newPathLiteral(pair, length);
		}
	}
	
	public int lastLiteral(){
		return litCounter-1;
	}

	public int getZeroLit(){
		if (zeroLiteral == -1){
			zeroLiteral = newOther("zero");
		}
		return zeroLiteral;
	}
	
	public int getNumOfVertices() {
		return numOfVertices;
	}

	public int getEdgeLiteral(int i, int j, Weight w) {
		Pair<Integer, Integer> edge = getOrderedPair(i, j);
		if (w.equals(Weight.LONG)){
			return longEdgesToLits.get(edge);
		}else{
			return shortEdgesToLits.get(edge);
		}
	}
	
	public boolean isEdge(int i, int j){
		Pair<Integer, Integer> pair = getOrderedPair(i, j);
		return longEdgesToLits.containsKey(pair);
	}
	
	public int newEdge(int i, int j, Weight w){
		int newLit = litCounter++;
		Pair<Integer, Integer> pair = getOrderedPair(i, j);
		if (w.equals(Weight.LONG)){
			longEdgesToLits.put(pair, newLit);
		}else{
			shortEdgesToLits.put(pair, newLit);
		}
		return newLit;
	}
	
	public Set<Pair<Integer, Integer>> getEdges(){
		return longEdgesToLits.keySet();
	}
	
	private static Pair<Integer, Integer> getOrderedPair(int i, int j) {
		if (i < j) {
			return new Pair<Integer, Integer>(i, j);
		}else{
			return new Pair<Integer, Integer>(j, i);
		}
	}
}